`include "macro.v"
module control (
    input wire [6:0] opcode,
    input wire [6:0] func7,
    input wire [2:0] func3,
    // input wire eq,
    // input wire lt,
    // input wire ltu,
    // output reg [1:0] npc_op,
    output reg [3:0] npc_sel,
    output reg rf_we,
    output reg [2:0] sext_op,
    output reg [1:0] wd_sel,
    output reg alua_sel,
    output reg alub_sel,
    output reg [3:0] alu_op,
    output reg wen,
    output wire [1:0] sd_sel,
    output wire [2:0] ld_sel,

    output wire debug_have_inst
);

wire R    = (opcode == 7'b0110011) ? 1'b1 : 1'b0;
wire I    = (opcode == 7'b0010011) ? 1'b1 : 1'b0;
wire lw   = (opcode == 7'b0000011) ? 1'b1 : 1'b0;
wire lui  = (opcode == 7'b0110111) ? 1'b1 : 1'b0;
wire sw   = (opcode == 7'b0100011) ? 1'b1 : 1'b0;
wire jalr = (opcode == 7'b1100111) ? 1'b1 : 1'b0;
wire jal  = (opcode == 7'b1101111) ? 1'b1 : 1'b0;
wire B    = (opcode == 7'b1100011) ? 1'b1 : 1'b0;
wire auipc= (opcode == 7'b0010111) ? 1'b1 : 1'b0;
assign debug_have_inst = R | I | lw | lui | sw | jalr | jal | B | auipc;


assign sd_sel = func3[1:0];
assign ld_sel = func3;

// npc_sel
always @(*) begin
    case(opcode)
        `OP_B : begin
            case(func3)
                `FUNC3_BEQ  : npc_sel = `NPC_BEQ;
                `FUNC3_BNE  : npc_sel = `NPC_BNE;
                `FUNC3_BLT  : npc_sel = `NPC_BLT;
                `FUNC3_BGE  : npc_sel = `NPC_BGE;
                `FUNC3_BLTU : npc_sel = `NPC_BLTU;
                `FUNC3_BGEU : npc_sel = `NPC_BGEU;
                default     : npc_sel = `NPC_NOP;
            endcase
        end
        `OP_JALR    : npc_sel = `NPC_JALR;
        `OP_J       : npc_sel = `NPC_JAL;
        default     : npc_sel = `NPC_NOP;
    endcase
end

// rf_we
always @(*) begin
    case(opcode)
        `OP_S       : rf_we = 'b0;
        `OP_B       : rf_we = 'b0;
        default     : rf_we = 'b1;
    endcase
end

// sext_op
always @(*) begin
    case(opcode)
        `OP_R       : sext_op = 'b0;
        `OP_I       : sext_op = `SEXT_I;
        `OP_LOAD    : sext_op = `SEXT_I;
        `OP_JALR    : sext_op = `SEXT_I;
        `OP_S       : sext_op = `SEXT_S;
        `OP_B       : sext_op = `SEXT_B;
        `OP_LUI     : sext_op = `SEXT_U;
        `OP_AUIPC   : sext_op = `SEXT_U;
        `OP_J       : sext_op = `SEXT_J;
        default     : sext_op = 'b0;
    endcase
end

// wd_sel
always @(*) begin
    case(opcode)
        `OP_R       : wd_sel = `WD_ALU;
        `OP_I       : wd_sel = `WD_ALU;
        `OP_LOAD    : wd_sel = `WD_RD;
        `OP_JALR    : wd_sel = `WD_PC4;
        `OP_AUIPC   : wd_sel = `WD_ALU;
        `OP_LUI     : wd_sel = `WD_EXT;
        `OP_J       : wd_sel = `WD_PC4;
        default     : wd_sel = `WD_ALU;
    endcase
end

// alua_sel
always @(*) begin
    case(opcode)
        `OP_AUIPC   : alua_sel = `ALUA_PC;
        default     : alua_sel = `ALUA_RD1;
    endcase
end

// alub_sel
always @(*) begin
    case(opcode)
        `OP_R       : alub_sel = `ALUB_RD2;
        `OP_B       : alub_sel = `ALUB_RD2;
        default     : alub_sel = `ALUB_EXT;
    endcase
end

//alu_op
wire [3:0] func_code = {func3, func7[5]};
always @(*) begin
    case(opcode)
        `OP_R : alu_op = func_code;
        `OP_I : begin
            if (func_code == `ALU_SRA) begin
                alu_op = func_code;
            end else begin
                alu_op = func_code & 4'b1110;
            end
        end
        default : alu_op = `ALU_ADD;
    endcase
end

// wen
always @(*) begin
    case(opcode)
        `OP_S       : wen = 1'b1;
        default     : wen = 1'b0;
    endcase
end

endmodule